# Copyright (c) 2008 Bartlett Publishing

#This machine has ALL of the pre-installed instructions in the instruction set.
#Many of these are removed in the "DefaultMachine" class.
class FullMachine < Machine
  #register_load(reg, val)
  define_instruction :register_load, :parameters => [machine_register_type, machine_integer_type] do |params|
    @registers[params[0]] = params[1]
  end

  #register_push_swp() [affects stack0 and register1
  define_instruction :register_push_swp do |params|
    @stacks[0] ||= []
    @stacks[0].push(@registers[1])
  end

  #register_push(reg, stack)
  define_instruction :register_push, :parameters => [machine_register_type, machine_stack_type] do |params|
    idx = params[1]
    @stacks[idx] ||= []
    @stacks[idx].push(@registers[params[0]])
  end

  #register_pop(reg, stack)
  define_instruction :register_pop, :parameters => [machine_register_type, machine_stack_type] do |params|
    stk = @stacks[params[1]] || []
    @registers[params[0]] = stk.pop.to_i
  end

  #register_add(reg1, reg2, reg_result)
  define_instruction :register_add, :parameters => [machine_register_type, machine_register_type, machine_register_type] do |params|
    @registers[params[2]] = @registers[params[0]].to_i + @registers[params[1]].to_i
  end

  #register_sub(rega, regb, reg_result) -> reg_result = rega - regb
  define_instruction :register_subtract, :parameters => [machine_register_type, machine_register_type, machine_register_type] do |params|
    @registers[params[2]] = @registers[params[0]].to_i - @registers[params[1]].to_i
  end

  #marker(val) - used for branch targets
  define_instruction :marker, :parameters => [machine_integer_type] do |params|
  end

  define_instruction :if_not_equal_single, :parameters => [machine_register_type, machine_register_type], :standard => false do |params|
    if @registers[params[0]] == @registers[params[1]]
      #If they are equal skip the next instruction
      @instruction_pointer = @instruction_pointer + 1
    end
  end

  #if_not_equal(rega, regb, jump_marker)
  define_instruction :jump_if_equal, :parameters => [machine_register_type, machine_register_type, machine_integer_type] do |params|
    if @registers[params[0]] == @registers[params[1]]
      @instruction_pointer = find_jump_marker(params[2])
      #NOTE - no need to back up, because we are jumping to the marker instruction
      #       the IP will be auto-moved to the appropriate location
    end
  end

  define_instruction :jump_if_less, :parameters => [machine_register_type, machine_register_type, machine_integer_type] do |params|
    if (@registers[params[0]].to_i) < (@registers[params[1]].to_i)
      @instruction_pointer = find_jump_marker(params[2])
    end
  end

  #jump_comparing(rega, regb, jump_less, jump_greater)
  # Nil is treated as 0
  define_instruction :jump_comparing, :parameters => [machine_register_type, machine_register_type, machine_integer_type, machine_integer_type] do |params|
    val1 = @registers[params[0]].to_i
    val2 = @registers[params[1]].to_i
    if val1 < val2
      @instruction_pointer = find_jump_marker(params[2])
    else
      if val1 > val2
        @instruction_pointer = find_jump_marker(params[3])
      else
        #Keep going
      end
    end
  end

  #register_print(reg)
  define_instruction :register_print, :parameters => [{:type => :integer, :range => default_register_range}] do |params|
    #puts @registers[params[0]]
  end

end
